package se.no.noko.engine.primitives;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.Map;

import se.no.noko.engine.core.Handles;
import se.no.noko.engine.core.LightCoordinates;
import se.no.noko.engine.core.Matrices;
import se.no.noko.engine.core.Renderable;
import se.no.noko.engine.utils.VertexUtils;
import android.opengl.GLES20;
import android.opengl.Matrix;

/**
 * Triangle primitive with methods for rendering
 * 
 * @author Artur
 * 
 */
public class Triangle implements Renderable {

	/** Size of float variables in ByteBuffer */
	private final int BYTES_PER_FLOAT = 4;

	/** How many bytes per float. */
	private final int POSITION_OFFSET = 0;

	/** Size of the position data in elements. */
	private final int POSITION_DATA_SIZE = 3;

	/** How many elements per vertex. */
	private final int STRIDE_BYTES = 7 * BYTES_PER_FLOAT;

	/** Offset of the color data. */
	private final int COLOR_OFFSET = 3;

	/** Size of the color data in elements. */
	private final int COLOR_DATA_SIZE = 4;

	private final FloatBuffer modelDataBuffer;

	/**
	 * 
	 * @param modelData
	 *            Array of floats with format [X1, Y1, Z1, R1, G1, B1, A1, ... ,
	 *            Xn, Yn, Zn, Rn, Gn, Bn, An] where X,Y,Z are position values of
	 *            vertice and R,G,B,A are color values for each vertice.
	 */
	public Triangle(float[] modelData) {
		if (modelData == null || modelData.length != 21) {
			throw new IllegalArgumentException();
		}
		modelDataBuffer = ByteBuffer
				.allocateDirect(modelData.length * BYTES_PER_FLOAT)
				.order(ByteOrder.nativeOrder()).asFloatBuffer();
		modelDataBuffer.put(modelData).position(0);
	}

	public Triangle(ColoredVertex v1, ColoredVertex v2, ColoredVertex v3) {
		float[] modelData = new float[21];
		VertexUtils.fillArrayWithColoredVertices(modelData,
				Arrays.asList(v1, v2, v3));
		modelDataBuffer = ByteBuffer
				.allocateDirect(modelData.length * BYTES_PER_FLOAT)
				.order(ByteOrder.nativeOrder()).asFloatBuffer();
		modelDataBuffer.put(modelData).position(0);
	}

	@Override
	public void render(Matrices matrices, Map<String, Integer> handles,
			LightCoordinates lightCoordinates) {
		modelDataBuffer.position(POSITION_OFFSET);
		GLES20.glVertexAttribPointer(handles.get(Handles.POSITION_HANDLE),
				POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, STRIDE_BYTES,
				modelDataBuffer);

		GLES20.glEnableVertexAttribArray(handles.get(Handles.POSITION_HANDLE));

		// Pass in the color information
		modelDataBuffer.position(COLOR_OFFSET);
		GLES20.glVertexAttribPointer(handles.get(Handles.COLOR_HANDLE),
				COLOR_DATA_SIZE, GLES20.GL_FLOAT, false, STRIDE_BYTES,
				modelDataBuffer);

		GLES20.glEnableVertexAttribArray(handles.get(Handles.COLOR_HANDLE));

		// This multiplies the view matrix by the model matrix, and stores the
		// result in the MVP matrix
		// (which currently contains model * view).
		Matrix.multiplyMM(matrices.getMvpMatrix(), 0, matrices.getViewMatrix(),
				0, matrices.getModelMatrix(), 0);

		// This multiplies the modelview matrix by the projection matrix, and
		// stores the result in the MVP matrix
		// (which now contains model * view * projection).
		Matrix.multiplyMM(matrices.getMvpMatrix(), 0,
				matrices.getProjectionMatrix(), 0, matrices.getMvpMatrix(), 0);

		GLES20.glUniformMatrix4fv(handles.get(Handles.MVP_MATRIX_HANDLE), 1,
				false, matrices.getMvpMatrix(), 0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);

	}

}
